<?php
namespace Core;

use ErrorException;

final class Error extends ErrorException {

	/**
	 * 写入错误日志
	 *
	 * @param string $message
	 * @return void
	 */
	static public function errorlog($message) {
		$message .= ", URI: {$_SERVER['REQUEST_URI']}" . (isset($_SERVER['HTTP_REFERER']) ? ", REFERER: {$_SERVER['HTTP_REFERER']}" : '');
		
		(HHO::$config['error.log'] == 1 || HHO::$config['error.log'] == 3) && error_log($message, 0);
		(HHO::$config['error.log'] == 2 || HHO::$config['error.log'] == 3) && error_log('[' . date('Y-m-d H:i:s') . "] $message" . PHP_EOL, 3, HHO::$config['error.file']);
	}

	/**
	 * 组装调试信息中的方法或函数调用参数
	 *
	 * @param array $args
	 * @return string
	 */
	static private function argument($agrs) {
		$argument = array();
		
		foreach ($agrs as $a) {
			switch (gettype($a)) {
				case 'string' :
					$argument[] = "'" . $a . "'";
					break;
				case 'object' :
					$argument[] = get_class($a);
					break;
				case 'array' :
					$argument[] = 'array[' . count($a) . ']';
					break;
				case 'boolean' :
					$argument[] = $a ? 'true' : 'false';
					break;
				case 'NULL' :
					$argument[] = 'NULL';
					break;
				case 'resource' :
					$argument[] = 'resource';
					break;
				default :
					$argument[] = $a;
			}
		}
		
		return implode(',', $argument);
	}

	/**
	 * 获得最近一个应用程序中的调用位置。
	 * 
	 * @param array $trace
	 * @return array
	 */
	private static function location($trace) {
		/**
		 * 调试 HHO 框架时，打开此返回值可显示框架代码错误信息。
		 */
		// return isset($trace[0]['file']) ? $trace[0] : $trace[1];
		foreach ($trace as $key => &$item) {
			if (!isset($item['class'])) {
				continue;
			}
			
			if (strstr($item['class'], '\\', TRUE) == 'Core') { // HHO 类。
				continue;
			}
			
			if (strpos($item['class'], '\\') === FALSE) { // PHP 类。
				continue;
			}
			
			return $trace[$key - 1];
		}
		
		return $trace[0];
	}

	/**
	 * 输出错误信息（页面），之后结束请求
	 *
	 * @return void
	 */
	private function display() {
		if (HHO::$config['debug']) {
			$trace = $this->getTrace();
			
			array_shift($trace);
			
			$x = self::location($trace);
			echo "<br /><span style='color: #C00000;'>[Location]<br />{$x['file']} on line {$x['line']}<br /><br />[Message]<br />" . htmlspecialchars($this->message) . '<br /><br />[Trace]<br />';
			
			foreach ($trace as $key => &$value) {
				isset($value['class']) || $value['class'] = '';
				isset($value['type']) || $value['type'] = '';
				
				$value['file'] = isset($value['file']) ? "{$value['file']} " : '';
				$value['line'] = isset($value['line']) ? "({$value['line']}) " : '';
				
				echo "#$key {$value['file']}{$value['line']}{$value['class']}{$value['type']}{$value['function']}(" . self::argument($value['args']) . ")<br />";
			}
			
			echo '</span>';
		} else {
			header('HTTP/1.1 500 Internal Server Error');
		}
		
		die();
	}

	/**
	 * 响应客户端 HTTP 404，之后结束请求
	 *
	 * @param string $message
	 * @return void
	 */
	static public function h404($message) {
		$trace = debug_backtrace();
		
		foreach ($trace as $k => $v) {
			if ($v['class'] . $v['type'] . $v['function'] == 'Core\HHO::run') {
				break;
			}
			
			array_shift($trace);
		}
		
		$info = array('message' => $message);
		
		if (HHO::$config['debug']) {
			$info['trace'] = '';
			
			foreach ($trace as $key => $value) {
				isset($value['class']) || $value['class'] = '';
				isset($value['type']) || $value['type'] = '';
				
				$info['trace'] .= "#$key {$value['file']} ({$value['line']}) {$value['class']}{$value['type']}{$value['function']}(" . self::argument($value['args']) . ')' . PHP_EOL;
			}
		}
		
		if (is_file(HHO::$config['404.html'])) {
			include HHO::$config['404.html'];
		} else {
			echo '<br /><span style="color: #C00000;">[Message]<br />' . htmlspecialchars($info['message']) . '<br /><br />[Trace]<br />' . nl2br(htmlspecialchars($info['trace'])) . '</span>';
		}
		
		$x = self::location($trace);
		self::errorlog("HHO Fatal error: $message in {$x['file']} on line {$x['line']}");
		
		die();
	}

	/**
	 * HHO错误处理句柄
	 *
	 * @param int $level
	 * @param string $message
	 * @param string $file
	 * @param int $line
	 * @return void
	 */
	public static function handle($level, $message, $file, $line) {
		switch ($level) {
			case E_USER_ERROR :
				$type = 'HHO Fatal error:';
				break;
			case E_WARNING :
				$type = 'PHP Warning:';
				break;
			case E_USER_WARNING :
				$type = 'HHO Warning:';
				break;
			case E_NOTICE :
				$type = 'PHP Notice:';
				break;
			case E_USER_NOTICE :
				$type = 'HHO Notice:';
				break;
			case E_DEPRECATED :
				$type = 'PHP Deprecated:';
				break;
			case E_USER_DEPRECATED :
				$type = 'HHO Deprecated:';
				break;
			case E_STRICT :
				$type = 'PHP Strict Standards:';
				break;
			case E_RECOVERABLE_ERROR :
				$type = 'PHP Catchable fatal error:';
				break;
			default :
				$type = "HHO Unknown error type($level):";
		}
		
		$x = self::location(debug_backtrace());
		$text = "$type $message in {$x['file']} on line {$x['line']}";
		
		self::errorlog($text);
		
		if ($level == E_USER_ERROR || $level == E_RECOVERABLE_ERROR) {
			$error = new self($message, 0, 0, $file, $line);
			$error->display();
		} elseif (HHO::$config['debug']) {
			echo PHP_EOL . $text;
		}
	}
}